import json
import traceback
import threading
from binascii import unhexlify
import time

import crcmod
import serial
import serial.tools.list_ports

from data_process import DataProcess


class SerialCommunicator:
    SUCCESS = "获取成功"
    FAIL = "获取失败"

    EN_TO_CHINESE = {
        "Online": "在线",
        "Offline": "离线",
        "Connected": "已连接",
        "NotConnected": "未连接",
        "socket1_off": "单通道",
        "socket1_mult": "双通道",
        "socket1_back": "主备",
        0: "正常",
        1: "性能优先",
        2: "平衡",
        "tcp_client": "TCP Client",
        "tcp_server": "TCP Server",
        "udp": "UDP",
    }

    CH_TO_ENGLISH = {v: k for k, v in EN_TO_CHINESE.items()}

    def __init__(self, port='COM7', baudrate=115200, timeout=10, slave_id=0x01):
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout
        self.slave_id = slave_id
        self.ser = None

    def send_command(self, command):
        print(command)
        start_address = int.from_bytes(command[2:2 + 2], byteorder='big')
        function_code = command[1]

        address_hex = f"{start_address:04X}"
        # print(start_address)
        # print(f'start_address:{address_hex}')

        try:
            if not self.ser or not self.ser.isOpen():
                self.ser = serial.Serial(
                    port=self.port,
                    baudrate=self.baudrate,
                    timeout=self.timeout
                )
                print(f"Connected to {self.port}")
            # 尝试写入命令
            thread = threading.Thread(target=self.write_to_serial_thread, args=(command,))
            thread.start()

            time.sleep(0.5)

            # 读取响应
            response = self.ser.read_all()
            if not response:
                print(f'{address_hex}:失败。未响应')
                return '未响应！'
            if function_code == 0x03:
                response = self.process_response_data(start_address, response)
            elif function_code == 0x06 or function_code == 0x10:
                # print(response)
                if len(response) == 8:
                    response = f'{address_hex}:写入成功。'
                    print(response)

                else:
                    response = f'{address_hex}:写入失败。'
                    print(response)
            return response

        except Exception as e:
            print(e, traceback.format_exc())
            print(f"Error: {address_hex}：{e}")
            # 最后一次重试仍然失败
            response = f"错误信息:{e}"
            if "list index out of range" in str(e):
                response = "指令异常，无法识别的指令，确认设备是否开启！"
            elif "could not open port" in str(e):
                response = "串口异常，串口被占用或者未启用！"


        finally:
            if self.ser:
                self.ser.close()

        return response

    def process_response_data(self, start_address, response_data):
        # 按每两个字节一组处理数据
        parsed_data = {}

        for i in range(3, len(response_data) - 2, 2):
            address = f"{(start_address + (i - 3) // 2):04X}"
            if address in ["0002", "0009", "0012"]:
                value = int.from_bytes(response_data[i:i + 4], byteorder='big')
            elif address in ["0003", "000A", "0013"]:
                continue
            else:
                value = int.from_bytes(response_data[i:i + 2], byteorder='big')
            parsed_data[address] = value
        data_process = DataProcess()
        data_process.process_data(parsed_data)
        return parsed_data

    def write_to_serial_thread(self, command):
        try:
            self.ser.write(command)
            # 如果需要，可以添加等待响应的代码
            # ...
        except serial.SerialException as e:
            print(f"Error in serial thread: {e}")

    @classmethod
    def detect_serial_ports(cls):
        return list(serial.tools.list_ports.comports())

    @staticmethod
    def crc16_count(hex_data):
        """
        CRC16计算
        Args:
            hex_data: 十六进制数据

        Returns: CRC16校验码

        """
        crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000)
        data = hex_data.replace(" ", "")
        result_data = hex(crc16(unhexlify(data))).upper()
        str_list = list(result_data)
        if len(str_list) < 6:
            str_list.insert(2, '0' * (6 - len(str_list)))  # 位数不足补0
        crc_data = "".join(str_list)
        return crc_data

    def modbus_command(self, function_code, address_h, address_l, byte_count_h, byte_count_l):
        """
        modbus指令拼接
        Args:
            dev_id: 设备ID
            function_code: 功能码
            address_h: 地址高位
            address_l: 地址低位
            byte_count_h: 字节数高位
            byte_count_l: 字节数低位

        Returns: modbus指令

        """
        command_array = [0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00]
        command_array[0] = self.slave_id
        command_array[1] = function_code
        command_array[2] = address_h
        command_array[3] = address_l
        command_array[4] = byte_count_h
        command_array[5] = byte_count_l
        crc_result = self.crc16_count(bytes(command_array)[0:(len(command_array) - 2)].hex())
        command_array[6] = int(crc_result[4:], 16)
        command_array[7] = int(crc_result[2:4], 16)
        return bytes(command_array)

    def modbus_command_10(self, function_code, address_h, address_l, regis_count_h, regis_count_l, byte_count, data_1_h,
                          data_1_l, data_2_h, data_2_l):
        """
        modbus_10功能码指令拼接
        Args:
            dev_id: 设备ID
            function_code: 功能码
            address_h: 起始地址高位
            address_l: 起始地址低位
            regis_count_h: 写入寄存器的个数高位
            regis_count_l: 写入寄存器的个数低位
            byte_count: 字节数
            data_1_h: 数据1高位
            data_1_l: 数据1低位
            data_2_h: 数据2高位
            data_2_l: 数据2低位

        Returns: modbus指令

        """
        # 01 10 0009 0002 04 xxxx xxxx
        command_array = [0x01, 0x10, 0x00, 0x09, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        command_array[0] = self.slave_id
        command_array[1] = function_code
        command_array[2] = address_h
        command_array[3] = address_l
        command_array[4] = regis_count_h
        command_array[5] = regis_count_l
        command_array[6] = byte_count
        command_array[7] = data_1_h
        command_array[8] = data_1_l
        command_array[9] = data_2_h
        command_array[10] = data_2_l
        crc_result = self.crc16_count(bytes(command_array)[0:(len(command_array) - 2)].hex())
        command_array[11] = int(crc_result[4:], 16)
        command_array[12] = int(crc_result[2:4], 16)
        return bytes(command_array)

    def modbus_command_10_multiple(self, function_code, address_h, address_l, regis_count_h, regis_count_l,
                                   *data_pairs):
        """
        modbus_10功能码指令拼接
        Args:
            function_code: 功能码
            address_h: 起始地址高位
            address_l: 起始地址低位
            regis_count_h: 写入寄存器的个数高位
            regis_count_l: 写入寄存器的个数低位
            *data_pairs: 可变数量的数据对 (data_h, data_l)

        Returns: modbus指令
        """
        # 计算字节数
        byte_count = len(data_pairs) * 2

        # 初始化命令数组
        command_array = [0x01, 0x10, address_h, address_l, regis_count_h, regis_count_l, byte_count]

        # 添加数据部分到命令数组
        for data_h, data_l in data_pairs:
            command_array.extend([data_h, data_l])

        # 计算CRC校验值
        crc_result = self.crc16_count(bytes(command_array).hex())

        # 将CRC值添加到命令数组末尾
        command_array.extend([int(crc_result[4:], 16), int(crc_result[2:4], 16)])

        return bytes(command_array)

    def write_to_serial(self, row_data):

        data_process = DataProcess()
        if row_data[2] in data_process.long_data_control_list:
            command_params = data_process.long_write_data_process(row_data)
            command = self.modbus_command_10(*command_params)
        elif row_data[2] in data_process.multi_mod_list:
            function_code, address_h, address_l, regis_count_h, regis_count_l, data_pairs = data_process.multi_write_data_process(
                row_data)
            command = self.modbus_command_10_multiple(function_code, address_h, address_l, regis_count_h,
                                                         regis_count_l,
                                                         *data_pairs)
        else:
            command_params = data_process.write_data_process(row_data)
            command = self.modbus_command(*command_params)
        responses = self.send_command(command)
        # print(responses)
        return responses


if __name__ == '__main__':
    manager = SerialCommunicator(port='COM13')

    data = ['some_data', 'more_data', '0016', [7, 8, 9]]
    data_process = DataProcess()
    function_code, address_h, address_l, regis_count_h, regis_count_l, data_pairs = data_process.multi_write_data_process(
        data)
    command = manager.modbus_command(0x03, 0x00, 0x1F,
                                     0x00, 0x01)
    command_10 = manager.modbus_command_10(0x10, 0x00, 0x00,
                                           0x00, 0x01, 0x04, 0x00, 0x08, 0x00, 0x09)
    command_11 = manager.modbus_command_10_multiple(0x10, 0x00, 0x00,
                                                    0x00, 0x01, (0x00, 0x08), (0x00, 0x09))
    command_12 = manager.modbus_command_10_multiple(function_code, address_h, address_l, regis_count_h, regis_count_l,
                                                    *data_pairs)

    # responses = manager.send_command(command)
    print(f"Final Response: {command_10}")
    print(f"Final Response: {command_11}")
    print(f"Final Response: {command_12}")
